using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Text;
using PostSharp.Aspects;

namespace Framework.Common.AOP
{
    #region
    ///// <summary>
    ///// Provides formatting string representing types, methods and fields. The
    ///// formatting strings may contain arguments like <c>{0}</c> 
    ///// filled at runtime with generic parameters or method arguments.
    ///// </summary>
    //internal static class Formatter
    //{
    //    /// <summary>
    //    /// Gets a formatting string representing a <see cref="Type"/>.
    //    /// </summary>
    //    /// <param name="type">A <see cref="Type"/>.</param>
    //    /// <returns>A formatting string representing the type
    //    /// where each generic type argument is represented as a
    //    /// formatting argument (e.g. <c>Dictionary&lt;{0},P1}&gt;</c>.
    //    /// </returns>
    //    public static string GettypeFormatString(Type type)
    //    {
    //        StringBuilder stringBuilder = new StringBuilder();

    //        // Build the format string for the declaring type.

    //        stringBuilder.Append(type.FullName);

    //        if (type.IsGenericTypeDefinition)
    //        {
    //            stringBuilder.Append("<");
    //            for (int i = 0; i < type.GetGenericArguments().Length; i++)
    //            {
    //                if (i > 0)
    //                    stringBuilder.Append(", ");
    //                stringBuilder.AppendFormat("{{{0}}}", i);
    //            }
    //            stringBuilder.Append(">");
    //        }
    //        return stringBuilder.ToString();
    //    }

    //    /// <summary>
    //    /// Gets the formatting strings representing a method.
    //    /// </summary>
    //    /// <param name="method">A <see cref="MethodBase"/>.</param>
    //    /// <returns></returns>
    //    public static MethodFormatStrings GetMethodFormatStrings(MethodBase method)
    //    {
    //        string typeFormat;
    //        bool typeIsGeneric;
    //        string methodFormat;
    //        bool methodIsGeneric;
    //        string parameterFormat;

    //        StringBuilder stringBuilder = new StringBuilder();

    //        typeFormat = GettypeFormatString(method.DeclaringType);
    //        typeIsGeneric = method.DeclaringType.IsGenericTypeDefinition;

    //        // Build the format string for the method name.
    //        stringBuilder.Length = 0;
    //        stringBuilder.Append("::");
    //        stringBuilder.Append(method.Name);
    //        if (method.IsGenericMethodDefinition)
    //        {
    //            methodIsGeneric = true;
    //            stringBuilder.Append("<");
    //            for (int i = 0; i < method.GetGenericArguments().Length; i++)
    //            {
    //                if (i > 0)
    //                    stringBuilder.Append(", ");
    //                stringBuilder.AppendFormat("{{{0}}}", i);
    //            }
    //            stringBuilder.Append(">");
    //        }
    //        else
    //        {
    //            methodIsGeneric = false;
    //        }
    //        methodFormat = stringBuilder.ToString();
    //        Console.WriteLine("methodFormat is " + methodFormat);
    //        // Build the format string for parameters.
    //        stringBuilder.Length = 0;
    //        ParameterInfo[] parameters = method.GetParameters();
    //        stringBuilder.Append("(");
    //        for (int i = 0; i < parameters.Length; i++)
    //        {
    //            if (i > 0)
    //            {
    //                stringBuilder.Append(", ");
    //            }
    //            stringBuilder.Append("{{{");
    //            stringBuilder.Append(i);
    //            stringBuilder.Append("}}}");
    //        }
    //        stringBuilder.Append(")");

    //        parameterFormat = stringBuilder.ToString();
    //        Console.WriteLine("parameterFormat is " + parameterFormat);

    //        return new MethodFormatStrings(typeFormat, typeIsGeneric, methodFormat, methodIsGeneric, parameterFormat);
    //    }

    //    /// <summary>
    //    /// Pads a string with a space, if not empty and not yet padded.
    //    /// </summary>
    //    /// <param name="prefix">A string.</param>
    //    /// <returns>A padded string.</returns>
    //    public static string NormalizePrefix(string prefix)
    //    {
    //        if (string.IsNullOrEmpty(prefix))
    //        {
    //            return "";
    //        }
    //        else if (prefix.EndsWith(" "))
    //        {
    //            return prefix;
    //        }
    //        else
    //        {
    //            return prefix + " ";
    //        }
    //    }

    //    public static string FormatString(string format, params object[] args)
    //    {
    //        if (args == null)
    //            return format;
    //        else
    //            return string.Format(format, args);
    //    }
    //}

    ///// <summary>
    ///// Set of 3 formatting string that, at runtime, represent a method and its
    ///// parameters.
    ///// </summary>
    //[Serializable]
    //internal class MethodFormatStrings
    //{
    //    private readonly string typeFormat;
    //    private readonly string methodFormat;
    //    private readonly string parameterFormat;
    //    private readonly bool typeIsGeneric;
    //    private readonly bool methodIsGeneric;

    //    /// <summary>
    //    /// Initializes a new <see cref="MethodFormatStrings"/>.
    //    /// </summary>
    //    /// <param name="typeFormat">
    //    /// The formatting string representing the type
    //    /// where each generic type argument is represented as a
    //    /// formatting argument (e.g. <c>Dictionary&lt;{0},P1}&gt;</c>.
    //    /// </param>
    //    /// <param name="methodFormat">
    //    /// The formatting string representing the method (but not the declaring type).
    //    /// where each generic method argument is represented as a
    //    /// formatting argument (e.g. <c>ToArray&lt;{0}&gt;</c>. 
    //    /// </param>
    //    /// <param name="parameterFormat">
    //    /// The formatting string representing the list of parameters, where each
    //    /// parameter is representing as a formatting argument 
    //    /// (e.g. <c>{0}, {1}</c>).        
    //    /// </param>
    //    /// <param name="methodIsGeneric">Indicates whether the method is generic.</param>
    //    /// <param name="typeIsGeneric">Indicates whether the type declaring the method is generic.</param>
    //    internal MethodFormatStrings(string typeFormat, bool typeIsGeneric,
    //                                  string methodFormat,
    //                                  bool methodIsGeneric,
    //                                  string parameterFormat)
    //    {
    //        this.typeFormat = typeFormat;
    //        this.methodFormat = methodFormat;
    //        this.parameterFormat = parameterFormat;
    //        this.typeIsGeneric = typeIsGeneric;
    //        this.methodIsGeneric = methodIsGeneric;
    //    }


    //    /// <summary>
    //    /// Gets a string representing a concrete method invocation.
    //    /// </summary>
    //    /// <param name="instance">Instance on which the method is invoked.</param>
    //    /// <param name="method">Invoked method.</param>
    //    /// <param name="invocationParameters">Concrete invocation parameters.</param>
    //    /// <returns>A representation of the method invocation.</returns>
    //    public string Format(
    //        object instance,
    //        MethodBase method,
    //        object[] invocationParameters)
    //    {
    //        string[] parts = new string[4]
    //            {
    //                typeIsGeneric
    //                    ? Formatter.FormatString( this.typeFormat, method.DeclaringType.GetGenericArguments() )
    //                    : this.typeFormat,
    //                methodIsGeneric
    //                    ? Formatter.FormatString( this.methodFormat, method.GetGenericArguments() )
    //                    : this.methodFormat,
    //                instance == null ? "" : string.Format( "{{{0}}}", instance ),
    //                Formatter.FormatString( this.parameterFormat, invocationParameters )
    //            };
    //        Console.WriteLine("typeFormat is " + typeFormat);
    //        Console.WriteLine("methodFormat is" + methodFormat);
    //        Console.WriteLine(parts[0]);
    //        Console.WriteLine(parts[1]);
    //        Console.WriteLine(parts[2]);
    //        Console.WriteLine(parts[3]);

    //        return string.Concat(parts);
    //    }
    //}
    #endregion

    /// <summary>
    /// formatter
    /// </summary>
    internal static class Formatter
    {
        #region const
        const char LeftAngleQuote = '<';
        const char RightAngleQuote = '>';

        const char LeftCurlyBrace = '{';
        const char RightCurlyBrace = '}';

        const char LeftParenthesis = '(';
        const char RightParenthesis = ')';

        const char DefaultSeperator = ',';
        const string MethodOfTypeChar = "::";
        #endregion

        #region format type
        /// <summary>
        /// get type format string
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static StringBuilder GetTypeFormatString(Type type)
        {
            var stringBuilder = new StringBuilder();
            stringBuilder.Append(type.FullName);
            if (type.IsGenericTypeDefinition)
            {
                stringBuilder.Append(LeftAngleQuote);
                Type[] argumentsTypes = type.GetGenericArguments();
                for (int i = 0; i < argumentsTypes.Length; i++)
                {
                    if (i > 0)
                        stringBuilder.Append(DefaultSeperator);
                    stringBuilder.Append(LeftCurlyBrace);
                    stringBuilder.Append(argumentsTypes[i]);
                    stringBuilder.Append(RightCurlyBrace);
                }
                stringBuilder.Append(RightAngleQuote);
            }
            return stringBuilder;
        }
        #endregion

        #region format method

        /// <summary>
        /// get method full name with args
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        public static StringBuilder GetMethodFormatString(MethodBase method)
        {
            Type methodType = method.DeclaringType;
            StringBuilder stringBuilder = GetTypeFormatString(methodType);
            stringBuilder.Append(MethodOfTypeChar);
            stringBuilder.Append(method.Name);
            if (method.IsGenericMethodDefinition || method.IsGenericMethod)
            {
                stringBuilder.Append(LeftAngleQuote);
                Type[] argumentsTypes = method.GetGenericArguments();
                for (int i = 0; i < argumentsTypes.Length; i++)
                {
                    if (i > 0)
                        stringBuilder.Append(DefaultSeperator);
                    stringBuilder.Append(argumentsTypes[i]);
                }
                stringBuilder.Append(RightAngleQuote);
            }

            ParameterInfo[] parameters = method.GetParameters();
            stringBuilder.Append(LeftParenthesis);
            for (int i = 0; i < parameters.Length; i++)
            {
                if (i > 0) stringBuilder.Append(DefaultSeperator);

                stringBuilder.Append(LeftCurlyBrace);
                stringBuilder.Append(i);
                stringBuilder.Append(RightCurlyBrace);
            }
            stringBuilder.Append(RightParenthesis);
            return stringBuilder;
        }

        /// <summary>
        /// Format
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public static string Format(MethodExecutionArgs args)
        {
            StringBuilder sb = methodDict.GetOrAdd(args.Method, GetMethodFormatString);
            return Format(args, sb.ToString());
        }

        /// <summary>
        /// Format
        /// </summary>
        /// <param name="args"></param>
        /// <param name="methodFormatString"></param>
        /// <returns></returns>
        public static string Format(MethodExecutionArgs args, string methodFormatString)
        {
            return Format(args.Instance, args.Arguments.ToArray(), methodFormatString);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="args"></param>
        /// <param name="methodFormatString"></param>
        /// <returns></returns>
        public static string Format(Object instance, Object[] args, string methodFormatString)
        {
            if (instance != null)
            {
                methodFormatString = string.Concat(methodFormatString, LeftParenthesis, instance, RightParenthesis);
            }
            if (args != null)
            {
                methodFormatString = string.Format(methodFormatString, args);
            }
            return methodFormatString;
        }

        #endregion

        #region constuctor
        static Formatter()
        {
            methodDict = new ConcurrentDictionary<MethodBase, StringBuilder>();
        }
        #endregion

        #region private static member
        private static readonly ConcurrentDictionary<MethodBase, StringBuilder> methodDict;
        #endregion
    }
}